package cn.patpang.core.framework.spring;

import cn.patpang.core.framework.spring.annotation.PathVariableRequestMapping;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Title PatBaseRequestMappingHandlerMapping.java
 * @Author PangPX
 * @Data 2017年6月15日 下午4:54:57
 * @自定义RequestMapping
 * @1、主要作用将url参数中的占位符号提取成一个数组，eg：/user/{id}/{name} = /user, {"id", "name"}
 * @2、获取Class名称作为url的模块名
 * @3、获取方法名作为url方法名字
 * @4、默认设置GET为RequestMethod
 */
public class PathVariableRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        // 非自定义内容不做操作
        if (null == AnnotatedElementUtils.findMergedAnnotation(method, PathVariableRequestMapping.class)) {
            return super.getMappingForMethod(method, handlerType);
        }

        RequestMappingInfo info, typeInfo;
        String className, classNameExceptAction, tmpString;

        // class name
        className = method.getDeclaringClass().getSimpleName();
        classNameExceptAction = className.replace("Controller", "");

        // 判断是否包含大小写字符，并将大写字符转化成小写，插入斜杠"/"
        tmpString = "";
        for (char c : classNameExceptAction.toCharArray()) {
            if (Character.isUpperCase(c)) {
                tmpString += "/";
            }
            tmpString += Character.toLowerCase(c);
        }
        classNameExceptAction = tmpString;

        info = createRequestMappingInfo(method, classNameExceptAction, method.getName(), method.getParameters());
        if (info != null) {
            typeInfo = createRequestMappingInfo(handlerType, classNameExceptAction, method.getName(), method.getParameters());
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
        }
        return info;
    }

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element, String controllerName, String methodName, Parameter[] parameters) {
        RequestMapping requestMapping;
        RequestCondition<?> condition;
        List<String> patternList;
        String[] values;
        String variableStr, tmpStr;
        RequestMethod[] methods = null;
        PathVariable pathVariable;

        requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        condition = (element instanceof Class<?> ? super.getCustomTypeCondition((Class<?>) element) : super.getCustomMethodCondition((Method) element));
        if (requestMapping == null) {
            return null;
        }

        values = requestMapping.value();

        // 参数拼装
        variableStr = "";
        if (null != parameters && parameters.length > 0) {
            for (Parameter parameter : parameters) {
                pathVariable = parameter.getAnnotation(PathVariable.class);
                if (null != pathVariable && StringUtils.isNotBlank(tmpStr = pathVariable.value().trim())) {
                    variableStr += "/{" + tmpStr + "}";
                }
            }
        }

        // path value 拼装
        patternList = new ArrayList<>();
        if (null != values && values.length > 0) {
            for (String value : values) {
                patternList.add(controllerName + value + variableStr);
            }
        } else {
            patternList.add(controllerName + "/" + methodName + variableStr);
        }

        methods = requestMapping.method();
        if (null == methods || methods.length <= 0) {
            methods = new RequestMethod[]{RequestMethod.GET};
        }

        logger.info("request mapping: " + Arrays.toString(patternList.toArray(new String[]{})) + "-------" + JSON.toJSON(methods));

        return new RequestMappingInfo(
                new PatternsRequestCondition(patternList.toArray(new String[]{}), getUrlPathHelper(), getPathMatcher(), true, true, new ArrayList<String>()),
                new RequestMethodsRequestCondition(methods),
                new ParamsRequestCondition(requestMapping.params()),
                new HeadersRequestCondition(requestMapping.headers()),
                new ConsumesRequestCondition(requestMapping.consumes(), requestMapping.headers()),
                new ProducesRequestCondition(requestMapping.produces(), requestMapping.headers(), new ContentNegotiationManager()),
                condition);
    }

}